{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "### 提升方法 AdaBoost\n",
    "\n",
    "> 理论 《统计学习方法》第8章 提升方法\n",
    ">\n",
    "> 代码 numpy version && torch version\n",
    ">\n",
    "> Python3.7\n",
    ">\n",
    "> created 2023/02/14\n",
    ">\n",
    "> author lyz\n",
    ">\n",
    "> email 2281250383@qq.com\n",
    "\n",
    "树上的例子"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(10,)\n",
      "(10,)\n"
     ]
    }
   ],
   "source": [
    "x_train = np.array([0,1,2,3,4,5,6,7,8,9])\n",
    "y_train = np.array([1,1,1,-1,-1,-1,1,1,1,-1])\n",
    "print(x_train.shape)\n",
    "print(y_train.shape)"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "outputs": [],
   "source": [
    "class BasicClassifier(object):\n",
    "    def __init__(self):\n",
    "        self.label = 1 # 方向\n",
    "        self.v = None  # label*x < label*v 是正例+1，反之负例-1\n",
    "        self.w = None"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "outputs": [],
   "source": [
    "class AdaBoost(object):\n",
    "    def __init__(self,n_estimators=3):\n",
    "        self.n_estimators = n_estimators\n",
    "        self.estimators = []\n",
    "\n",
    "    def fit(self,x,y):\n",
    "        size = len(x)\n",
    "        # 获取特征值并去重\n",
    "        unique_values = np.unique(x)\n",
    "        sorted(unique_values)\n",
    "        # 初始化权重\n",
    "        w = np.full(size,1/size)\n",
    "        self.estimators = []\n",
    "        for ixe in range(self.n_estimators):\n",
    "            # print('dataset weight',w)\n",
    "            min_error = float('inf')\n",
    "            # 训练弱分类器\n",
    "            basic_classifier = BasicClassifier()\n",
    "            for threshold in unique_values:\n",
    "                # 这个阈值的设置不是很对，就照着课本上的来，为了复现吧\n",
    "                threshold += 0.5\n",
    "                pred = np.ones_like(y)\n",
    "                pred[x<threshold] = -1\n",
    "                p = 1\n",
    "                error = sum(w[pred!=y])\n",
    "                if error > 0.5:\n",
    "                    error = 1-error\n",
    "                    p = -1\n",
    "\n",
    "                if error<min_error:\n",
    "                    min_error = error\n",
    "                    basic_classifier.label = p\n",
    "                    basic_classifier.v = threshold\n",
    "\n",
    "            print('basic_classifier error {:.2f}; v {}'.format(min_error,basic_classifier.v))\n",
    "            basic_classifier.w = 1/2 * np.log((1-min_error)/(min_error+1e-9))\n",
    "            pred = np.ones_like(y)\n",
    "            negative_idx = (basic_classifier.label * x < basic_classifier.label * basic_classifier.v)\n",
    "            pred[negative_idx] = -1\n",
    "            w *= np.exp(-1*basic_classifier.w * y * pred)\n",
    "            w /= sum(w)\n",
    "            self.estimators.append(basic_classifier)\n",
    "\n",
    "            print('basic_classifier {}, param: label {}; v {:.2f}; w {:.2f}'.format(ixe,basic_classifier.label,basic_classifier.v,basic_classifier.w))\n",
    "            print()\n",
    "\n",
    "    def predict(self,x):\n",
    "        size = len(x)\n",
    "        y_pred = np.ones_like(x)\n",
    "        for estimator in self.estimators:\n",
    "            pred = np.ones_like(x)\n",
    "            negative_idx = (estimator.label * x < estimator.label * estimator.v)\n",
    "            pred[negative_idx] = -1\n",
    "            y_pred += estimator.w * pred\n",
    "        y_pred = np.sign(y_pred)\n",
    "        return y_pred"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "basic_classifier error 0.30; v 2.5\n",
      "basic_classifier 0, param: label -1; v 2.50; w 0.42\n",
      "\n",
      "basic_classifier error 0.21; v 8.5\n",
      "basic_classifier 1, param: label -1; v 8.50; w 0.65\n",
      "\n",
      "basic_classifier error 0.18; v 5.5\n",
      "basic_classifier 2, param: label 1; v 5.50; w 0.75\n",
      "\n"
     ]
    }
   ],
   "source": [
    "classifier = AdaBoost()\n",
    "classifier.fit(x_train,y_train)"
   ],
   "metadata": {
    "collapsed": false
   }
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
